package personnel.department.dao;

import static personnel.department.dao.HibernateUtils.getSession;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.tuple.Pair;
import org.hibernate.Query;
import org.reflections.Reflections;

import javassist.tools.reflect.Reflection;

import com.google.common.collect.Maps;

import personnel.department.dao.entity.AdoptedToStructural;
import personnel.department.dao.entity.Career;
import personnel.department.dao.entity.Certificate;
import personnel.department.dao.entity.Degree;
import personnel.department.dao.entity.Diplom;
import personnel.department.dao.entity.Education;
import personnel.department.dao.entity.Employee;
import personnel.department.dao.entity.EmployeeType;
import personnel.department.dao.entity.Internship;
import personnel.department.dao.entity.Pasport;
import personnel.department.dao.entity.PersonalData;
import personnel.department.dao.entity.Qualification;
import personnel.department.dao.entity.Quit;
import personnel.department.dao.entity.RecordService;
import personnel.department.dao.entity.Retraining;
import personnel.department.dao.entity.StructualSubdivision;
import personnel.department.dao.entity.Translation;
import personnel.department.dao.entity.UniqueObject;
import personnel.department.dao.entity.Validation;
import personnel.department.dao.entity.Work;

public class QueryBuilder {
	
	
	private static final String ADOPTED_SUFFIX = "adopt";
	private static final String CAREER_SUFFIX = "career";
	private static final String CERTIFICATE_SUFFIX = "cert";
	private static final String DEGREE_SUFFIX = "degree";
	private static final String DIPLOM_SUFFIX = "diplom";
	private static final String EDUCATION_SUFFIX = "educ";
	private static final String EMPLOYEE_SUFFIX = "e";
	private static final String EMPLOYEE_TYPE_SUFFIX = "empType";
	private static final String INTERNSHIP_SUFFIX = "intern";
	private static final String PASPORT_SUFFIX = "pasp";
	private static final String PERSONAL_DATA_SUFFIX = "perData";
	private static final String QUALIFICATION_SUFFIX = "qualific";
	private static final String QUIT_SUFFIX = "quit";
	private static final String RECORD_SERVICE_SUFFIX = "recServ";
	private static final String RETRAINING_SUFFIX = "retrain";
	private static final String STRUCTUAL_SUBDIVISION_SUFFIX = "strSub";
	private static final String TRANSLATION_SUFFIX = "transl";
	private static final String VALIDATION_SUFFIX = "valid";
	private static final String WORK_SUFFIX = "work";
	
	private static final Map<Class<?>, String> aliasByClass = Maps.newHashMap(); 
	
	static {
		
		aliasByClass.put(Employee.class, EMPLOYEE_SUFFIX);
		aliasByClass.put(EmployeeType.class, EMPLOYEE_TYPE_SUFFIX);
		aliasByClass.put(AdoptedToStructural.class, ADOPTED_SUFFIX);
		aliasByClass.put(Career.class, CAREER_SUFFIX);
		aliasByClass.put(Certificate.class, CERTIFICATE_SUFFIX);
		aliasByClass.put(Degree.class, DEGREE_SUFFIX);
		aliasByClass.put(Diplom.class, DIPLOM_SUFFIX);
		aliasByClass.put(Education.class, EDUCATION_SUFFIX);
		aliasByClass.put(Internship.class, INTERNSHIP_SUFFIX);
		aliasByClass.put(Pasport.class, PASPORT_SUFFIX);
		aliasByClass.put(PersonalData.class, PERSONAL_DATA_SUFFIX);
		aliasByClass.put(Qualification.class, QUALIFICATION_SUFFIX);
		aliasByClass.put(Quit.class, QUIT_SUFFIX);
		aliasByClass.put(RecordService.class, RECORD_SERVICE_SUFFIX);
		aliasByClass.put(Retraining.class, RETRAINING_SUFFIX);
		aliasByClass.put(StructualSubdivision.class, STRUCTUAL_SUBDIVISION_SUFFIX);
		aliasByClass.put(Translation.class, TRANSLATION_SUFFIX);
		aliasByClass.put(Validation.class, VALIDATION_SUFFIX);
		aliasByClass.put(Work.class, WORK_SUFFIX);
	}
	
	private Map<Field, Object> searchCriteriafieldsAndValue;
	private Map<String, Object> paramByName = Maps.newHashMap();

	public QueryBuilder(Map<Field, Object> searchCriteriafields) {
		this.searchCriteriafieldsAndValue = searchCriteriafields;
	}
	
	private Map<String, Object> getParams() {
		return paramByName;
	}
	
	public List<Employee> execute() {
		System.out.println(getHql());
		Query query = getSession().createQuery(getHql());
		for (String paramName : paramByName.keySet()) {
			query.setParameter(paramName, paramByName.get(paramName));
		}
		System.out.println(query.getQueryString());
		List<Employee> result = query.list();
		return result;
	}
	
	private String getHql() {
		return getSelect() + getFrom() + getWhere() + " ORDER BY e.surname";
	}

	private String getSelect() {
		return "SELECT e ";
	}
	
	private String getFrom() {
		return "FROM Employee e JOIN e.subdivision " + STRUCTUAL_SUBDIVISION_SUFFIX + 
				" LEFT JOIN e.personalData " + PERSONAL_DATA_SUFFIX + 
				" LEFT JOIN " + PERSONAL_DATA_SUFFIX + ".pasport " + PASPORT_SUFFIX  + 
				" LEFT JOIN " + PERSONAL_DATA_SUFFIX + ".recordService " + RECORD_SERVICE_SUFFIX + 
				" LEFT JOIN e.education " + EDUCATION_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".certificates " + CERTIFICATE_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".qualifications " + QUALIFICATION_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".retrainings " + RETRAINING_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".validations " + VALIDATION_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".degrees " + DEGREE_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".medicDiplom " + DIPLOM_SUFFIX + 
				" LEFT JOIN " + EDUCATION_SUFFIX + ".internship " + INTERNSHIP_SUFFIX +
				" LEFT JOIN e.career " + CAREER_SUFFIX + 
				" LEFT JOIN " + CAREER_SUFFIX + ".adoptedToStructural " + ADOPTED_SUFFIX + 
				" LEFT JOIN " + CAREER_SUFFIX + ".translations " + TRANSLATION_SUFFIX + 
				" LEFT JOIN e.type " + EMPLOYEE_TYPE_SUFFIX; 
				
		
	}
	
	private String getWhere() {
		if (searchCriteriafieldsAndValue.isEmpty()) {
			return "";
		}
		int count = 1;
		StringBuilder b = new StringBuilder(" WHERE ");
		for (Field field : searchCriteriafieldsAndValue.keySet()) {
			String suffix = aliasByClass.get(field.getDeclaringClass());
			String paramName = "param" + count++;
			b.append(" AND " + suffix + "." + field.getName() + " = :" + paramName);
			paramByName.put(paramName, searchCriteriafieldsAndValue.get(field));
		}
		return b.toString().replaceFirst("AND", "");
	}
	
}
